# coding=utf-8
# __author__ = "James Zhang"
# !/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function, division
from __future__ import unicode_literals
from decimal import Decimal
import datetime
import six


class Promise(object):
    pass

if six.PY3:
    from urllib.parse import unquote_to_bytes


class TornadoUnicodeDecodeError(UnicodeEncodeError):
    def __init__(self, obj, *args):
        self.obj = obj
        UnicodeEncodeError.__init__(self, *args)

    def __str__(self):
        original = UnicodeEncodeError.__str__(self)
        return "%s. You passsed %r (%s)" % (original, self.obj, type(self.obj))

_PROTECTED_TYPES = six.integer_types + (type(None), float, Decimal,
                                        datetime.datetime, datetime.date, datetime.time)


def is_protected_type(obj):
    return isinstance(obj, _PROTECTED_TYPES)


def force_bytes(s, encoding='utf-8', string_only=False, errors='strict'):
    if isinstance(s, six.text_type):
        return s
    if string_only and is_protected_type(s):
        return s
    try:
        if not isinstance(s, six.string_types):
            if six.PY3:
                if isinstance(s, bytes):
                    s = six.text_type(s, encoding, errors)
                else:
                    s = six.text_type(s)
            elif hasattr(s, "__unicode__"):
                s = six.text_type(s)
            else:
                s = six.text_type(bytes(s), encoding, errors)
        else:
            s = s.decode(encoding, errors)
    except UnicodeEncodeError as e:
        if not isinstance(s, Exception):
            raise TornadoUnicodeDecodeError(s, *e.args)
        else:
            s = " ".join(force_bytes(arg, encoding, string_only, errors) for arg in s)
    return s


def force_text(s, encoding='utf-8', strings_only=False, errors='strict'):
    if isinstance(s, six.text_type):
        return s
    if strings_only and is_protected_type(s):
        return s
    try:
        if not isinstance(s, six.string_types):
            if six.PY3:
                if isinstance(s, bytes):
                    s = six.text_type(s, encoding, errors)
                else:
                    s = six.text_type(s)
            elif hasattr(s, "__unicode__"):
                s = six.text_type(s)
            else:
                s = six.text_type(bytes(s), encoding, errors)
        else:
            s = s.decode(encoding, errors)
    except UnicodeEncodeError as e:
        if not isinstance(s, Exception):
            raise TornadoUnicodeDecodeError(s, *e.args)
        else:
            s = ' '.join(force_text(arg, encoding, strings_only, errors) for arg in s)
    return s


def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    通过特别的"编码"返回一个text型(unicode)字符串,
    如果strings_only=True, 将不会转换非字符串类型(如objects)

    :param s:
    :param encoding:
    :param strings_only:
    :param errors:
    :return:
    """
    if isinstance(s, Promise):
        return s
    return force_text(s, encoding, strings_only, errors)


def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    通过特别的"编码"返回一个bytes字符串,
    如果strings_only=True, 将不会转换非字符串类型(如objects)

    :param s:
    :param encoding:
    :param strings_only:
    :param errors:
    :return:
    """
    if isinstance(s, Promise):
        return s
    return force_bytes(s, encoding, strings_only, errors)
